package ai.people.core.nebula.util;

import ai.people.core.nebula.entity.NebulaGraph;
import ai.people.core.nebula.entity.NebulaTransaction;
import ai.people.core.nebula.holder.NebulaSessionContextHolder;
import ai.people.core.nebula.request.GraphPathRequest;
import ai.people.core.nebula.tx.NebulaTransactionUtils;
import ai.people.netmon.framework.constant.GraphConstant;
import ai.people.netmon.framework.constant.GraphDirection;
import ai.people.netmon.utils.StringPool;
import com.alibaba.fastjson.JSONObject;
import com.vesoft.nebula.Path;
import com.vesoft.nebula.Step;
import com.vesoft.nebula.client.graph.data.*;
import com.vesoft.nebula.client.graph.exception.IOErrorException;
import com.vesoft.nebula.client.graph.net.Session;
import com.vesoft.nebula.client.graph.net.SessionWrapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * nebula graph utils
 *
 * @author zhenglin
 * @date 2022/4/11
 */
public class NebulaQueryUtils {

    private static final Logger log = LoggerFactory.getLogger(NebulaQueryUtils.class);

    /**
     * 通过vid查询点属性
     *
     * @param vid vid
     * @return {@link String}
     */
    private static String buildVidQuery(String vid) {
        String gql = "MATCH (v) WHERE id(v) == \"" + vid + "\" RETURN v;";
        log(gql);
        return gql;
    }

    /**
     * 日志
     *
     * @param gql gql
     */
    private static void log(String gql) {
        if (StringUtils.isBlank(gql)) {
            return;
        }
        log.info("nGQL: {}", gql);
    }

    /**
     * 运行gql 无返回值
     *
     * @param session 会话
     * @param gql     gql
     * @throws IOErrorException ioerror异常那么
     */
    public static void execute(Session session, String gql) throws IOErrorException {
        ResultSet execute = session.execute(gql);
        checkErr(execute);
    }

    /**
     * 运行gql 无返回值
     *
     * @param session 会话
     * @param gql     gql
     * @return boolean
     * @throws IOErrorException ioerror异常那么
     */
    public static boolean execute(SessionWrapper session, String gql) throws IOErrorException {
        ResultSet execute = session.execute(gql);
        return checkErr(execute);
    }

    /**
     * 批量保存
     *
     * @param session 会话
     * @param ngls    天然气凝析液
     * @return boolean
     * @throws IOErrorException             ioerror异常那么
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static boolean executeBatch(SessionWrapper session, Collection ngls) throws IOErrorException, UnsupportedEncodingException {
        if (CollectionUtils.isEmpty(ngls)) {
            return false;
        }
        StringBuilder sb = new StringBuilder();
        for (Object ngl : ngls) {
            sb.append(ngl);
        }
        String ngl = sb.toString();
        log(ngl);
        addToTx(ngl);
        ResultSet execute = session.execute(ngl);
        return checkErr(execute);
    }

    /**
     * 添加到事务 auto rollback
     *
     * @param gql gql
     *
     * {@link ai.people.core.nebula.aspect.NebulaAutoReleaseAspect# rollback}
     */
    private static void addToTx(String gql) {
        NebulaTransaction tx = NebulaSessionContextHolder.getTx();
        if (tx != null) {
            List<NebulaTransaction.Gql> gqls = tx.getGqls();
            if (CollectionUtils.isEmpty(gqls)) {
                gqls = new ArrayList<>();
            }
            gqls.add(NebulaTransaction.Gql.builder()
                    .commitGql(gql)
                    .rollbackGql(NebulaTransactionUtils.getRollbackGql(gql))
                    .build());
            tx.setGqls(gqls);
        }
    }

   

    /**
     * 查询图路径
     *
     * @param sessionWrapper 会话包装
     * @param rootVid        根vid
     * @param steps          步骤
     * @param limit          限制
     * @param direction      方向
     * @param edges          边缘
     * @return {@link NebulaGraph}
     * @throws IOErrorException             ioerror异常那么
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static NebulaGraph path(SessionWrapper sessionWrapper,String rootVid, int steps, Integer limit, String direction, String... edges) throws IOErrorException, UnsupportedEncodingException {
        String pathQuery = buildPathQuery(rootVid, steps, limit, direction, edges);
        log(pathQuery);
        ResultSet resultSet = sessionWrapper.execute(pathQuery);
        checkErr(resultSet);
        return doHandlePathResult(resultSet);
    }

    /**
     * 查询图路径
     *
     * @param sessionWrapper 会话包装
     * @param pathGql        路径gql
     * @return {@link NebulaGraph}
     * @throws IOErrorException             ioerror异常那么
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static NebulaGraph path(SessionWrapper sessionWrapper,String pathGql) throws IOErrorException, UnsupportedEncodingException {
        log(pathGql);
        ResultSet resultSet = sessionWrapper.execute(pathGql);
        checkErr(resultSet);
        return doHandlePathResult(resultSet);
    }

    /**
     * 遍历图路径
     * <p>
     * 替换方法参考 {@link ai.people.core.nebula.util.NebulaQueryUtils#recursionGetPath}
     *
     * @param session 会话
     * @param request 请求
     * @return {@link NebulaGraph}
     * @throws IOErrorException             ioerror异常那么
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    @Deprecated
    public static NebulaGraph path(SessionWrapper session, GraphPathRequest request) throws IOErrorException, UnsupportedEncodingException {
        String gql = buildPathQuery(request);
        ResultSet execute = session.execute(gql);
        checkErr(execute);
        return doHandlePathResult(execute);
    }

    /**
     * 遍历点的路径
     *
     * 处理遍历图返回的路径数据
     *
     * @param resultSet 结果集
     * @return {@link NebulaGraph}
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static NebulaGraph doHandlePathResult(ResultSet resultSet) throws UnsupportedEncodingException {
        NebulaGraph result = new NebulaGraph();
        Map<String, NebulaGraph.NebulaNode> map = new HashMap<>();
        int len = resultSet.rowsSize();
        NebulaGraph.NebulaNode rootNode = null;
        LinkedHashSet<NebulaGraph.NebulaPath> nebulaPaths = new LinkedHashSet<>();
        LinkedHashSet<NebulaGraph.NebulaNode> nebulaNodes = new LinkedHashSet<>();
        for (int i = 0; i < len; i++) {
            ResultSet.Record record = resultSet.rowValues(i);
            for (ValueWrapper value : record.values()) {
                if (value.isPath()) {
                    PathWrapper pathWrapper = value.asPath();
                    List<Relationship> relationships = pathWrapper.getRelationships();

                    String decodeType = pathWrapper.getDecodeType();
                    int timezoneOffset = pathWrapper.getTimezoneOffset();
                    Node startNode = pathWrapper.getStartNode();
                    String rootId = startNode.getId().asString();

                    rootNode = new NebulaGraph.NebulaNode(startNode);

                    result.setRootNode(rootNode);
                    Path path = value.getValue().getPVal();

                    map.put(rootId, rootNode);

                    for (int j = 0; j < relationships.size(); j++) {
                        Relationship relationship = relationships.get(j);

                        String edgeName = relationship.edgeName();
                        HashMap<String, ValueWrapper> properties = relationship.properties();

                        NebulaGraph.NebulaNode edge = new NebulaGraph.NebulaNode(edgeName, properties);

                        Step step = path.steps.get(j);
                        Node node = (Node) new Node(step.dst).setDecodeType(decodeType).setTimezoneOffset(timezoneOffset);
                        String nodeName = node.getId().asString();
                        NebulaGraph.NebulaNode stepNode = new NebulaGraph.NebulaNode(node);
                        map.put(nodeName, stepNode);

                        NebulaGraph.NebulaNode from = map.get(relationship.srcId().asString());
                        NebulaGraph.NebulaNode to = map.get(relationship.dstId().asString());
                        nebulaPaths.add(new NebulaGraph.NebulaPath(from, to, edge));
                        nebulaNodes.add(from);
                        nebulaNodes.add(to);
                    }
                }
            }
        }
        result.setRootNode(rootNode);
        result.setPaths(nebulaPaths);
        result.setNodesVidSet(map.keySet());
        result.setNodeSet(nebulaNodes);
        return result;
    }

    /**
     * 获取vertex
     *
     * @param session 会话
     * @param vid     从视频
     * @return {@link NebulaGraph.NebulaNode}
     * @throws IOErrorException             ioerror异常那么
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static NebulaGraph.NebulaNode getVertexByVid(SessionWrapper session, String vid) throws IOErrorException, UnsupportedEncodingException {
        String gql = buildVidQuery(vid);
        ResultSet result = session.execute(gql);
        checkErr(result);
        for (int i = 0; i < result.rowsSize(); i++) {
            ResultSet.Record record = result.rowValues(i);
            for (ValueWrapper value : record.values()) {
                if (value.isVertex()) {
                    Node node = value.asNode();
                    return new NebulaGraph.NebulaNode(node);
                }
            }
        }
        return null;
    }

    /**
     * 得到节点所有属性
     *
     * @param node   节点
     * @param tClass t类
     * @return {@link T}
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static <T> T getNodeProperties(Node node, Class<T> tClass) throws UnsupportedEncodingException {
        JSONObject nodeAllTagValue = (JSONObject) getNodeProperties(node);
        return nodeAllTagValue.toJavaObject(tClass);
    }

    /**
     * 得到节点所有属性
     *
     * @param node 节点
     * @return {@link JSONObject}
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static Map<String, Object> getNodeProperties(Node node) throws UnsupportedEncodingException {
        List<String> tagNames = node.tagNames();
        Map<String, Object> tagNameValue = new JSONObject(tagNames.size());
        for (String name : tagNames) {
            Map<String, ValueWrapper> props = node.properties(name);
            props.forEach((k, v) -> {
                try {
                    tagNameValue.put(k, getValue(v));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            });
        }
        return tagNameValue;
    }


    /**
     * 解决一个vid 有多个tag的情况
     *
     * @param node 节点
     * @return {@link Map}<{@link String}, {@link Object}>
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static Map<String, Object> getNodeAllTagValueOrigin(Node node) throws UnsupportedEncodingException {
        List<String> tagNames = node.tagNames();
        Map<String, Object> tagNameValue = new JSONObject(tagNames.size());
        for (String name : tagNames) {
            Map<String, Object> tagMap = new HashMap<>();
            Map<String, ValueWrapper> props = node.properties(name);
            props.forEach((k, v) -> {
                try {
                    tagMap.put(k, getValue(v));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            });
            tagNameValue.put(name, tagMap);
        }
        return tagNameValue;
    }

    /**
     * 得到节点所有属性
     *
     * @param valueWrapper 价值包装
     * @return {@link JSONObject}
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static Map<String, Object> getProperties(Map<String, ValueWrapper> valueWrapper) throws UnsupportedEncodingException {
        Map<String, Object> tagNameValue = new HashMap<>();
        valueWrapper.forEach((k, v) -> {
            try {
                tagNameValue.put(k, getValue(v));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        });
        return tagNameValue;
    }

    /**
     * 获得值
     *
     * @param value 价值
     * @return {@link Object}
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static Object getValue(ValueWrapper value) throws UnsupportedEncodingException {
        if (value.isLong()) {
            return value.asLong();
        }
        if (value.isBoolean()) {
            return value.asBoolean();
        }
        if (value.isDouble()) {
            return value.asDouble();
        }
        if (value.isString()) {
            return value.asString();
        }
        if (value.isTime()) {
            return value.asTime();
        }
        if (value.isDate()) {
            return value.asDate();
        }
        if (value.isDateTime()) {
            return value.asDateTime();
        }
        if (value.isVertex()) {
            return value.asNode();
        }
        if (value.isEdge()) {
            return value.asRelationship();
        }
        if (value.isPath()) {
            return value.asPath();
        }
        if (value.isList()) {
            return value.asList();
        }
        if (value.isSet()) {
            return value.asSet();
        }
        if (value.isMap()) {
            return value.asMap();
        }
        return null;
    }

 

    

    /**
     * 检查错误
     *
     * @param resultSet 结果集
     * @return boolean
     */
    public static boolean checkErr(ResultSet resultSet) {
        String errorMessage = resultSet.getErrorMessage();
        if (StringUtils.isNotBlank(errorMessage)) {
            int errorCode = resultSet.getErrorCode();
            log.error("query nebula err code:{} msg:{}", errorCode, errorMessage);
            return false;
        }
        return true;
    }


    

    /**
     * 插入点
     *
     * @param session  会话
     * @param vid      vid
     * @param tagName  标签名
     * @param tagValue 标签值
     * @return boolean
     * @throws IOErrorException             ioerror异常那么
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static boolean insertVertex(SessionWrapper session, String vid, String tagName, Object tagValue) throws IOErrorException, UnsupportedEncodingException {
        JSONObject jsonObject = new JSONObject();
        if (tagValue != null) {
            String jsonString = JSONObject.toJSONString(tagValue);
            jsonString = replaceAllFastJSONStringLineCharacters(jsonString);
            jsonObject = JSONObject.parseObject(jsonString);
        }
        String createTag = buildInsertVertexQuery(tagName, vid, jsonObject);
        log(createTag);
//        addToTx(createTag);
        ResultSet execute = session.execute(createTag);
        return checkErr(execute);
    }


    /**
     * 插入边
     *
     * @param session   会话
     * @param srcVid    src vid
     * @param dstVid    dst vid
     * @param edgeName  边缘名字
     * @param edgeValue 边缘值
     * @return boolean
     * @throws IOErrorException             ioerror异常那么
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static boolean insertEdge(SessionWrapper session, String srcVid, String dstVid, String edgeName, Object edgeValue) throws IOErrorException, UnsupportedEncodingException {
        JSONObject jsonObject = edgeValue == null ? new JSONObject() : JSONObject.parseObject(JSONObject.toJSONString(edgeValue));
        String createEdge = buildCreateEdgeQuery(srcVid, dstVid, edgeName, jsonObject);
        log(createEdge);
//        addToTx(createEdge);
        ResultSet execute = session.execute(createEdge);
        return checkErr(execute);
    }


    /**
     * 更新点
     *
     * @param session 会话
     * @param vid     vid
     * @param tagName 标签名
     * @param data    数据
     * @return boolean
     * @throws IOErrorException ioerror异常那么
     */
    public static boolean updateVertex(SessionWrapper session, String vid, String tagName, Map<String, Object> data) throws IOErrorException {
        if (data == null || data.isEmpty()) {
            return false;
        }
        String updateTag = buildUpdateVertexQuery(vid, tagName, data);
        log(updateTag);
        ResultSet execute = session.execute(updateTag);
        return checkErr(execute);
    }

    /**
     * 更新点  无则插入
     *
     * @param session 会话
     * @param vid     从视频
     * @param tagName 标签名
     * @param data    数据
     * @return boolean
     * @throws IOErrorException ioerror异常那么
     */
    public static boolean upsertVertex(SessionWrapper session, String vid, String tagName, Map<String, Object> data) throws IOErrorException {
        if (data == null || data.isEmpty()) {
            return false;
        }
        String upsertTag = buildUpsertVertexQuery(vid, tagName, data);
        log(upsertTag);
        ResultSet execute = session.execute(upsertTag);
        return checkErr(execute);
    }

    /**
     * 更新点  无则插入
     *
     * @param session 会话
     * @param vid     从视频
     * @param tagName 标签名
     * @param data    数据
     * @return boolean
     * @throws IOErrorException ioerror异常那么
     */
    public static boolean upsertVertex(SessionWrapper session, String vid, String tagName, Object data) throws IOErrorException {
        if (data == null) {
            return false;
        }
        data = replaceAllFastJSONStringLineCharacters(JSONObject.toJSONString(data));
        JSONObject jsonObject = JSONObject.parseObject(data.toString());
        return upsertVertex(session, vid, tagName, jsonObject);
    }

    /**
     * 删除点
     *
     * @param session  会话
     * @param withEdge 与边缘
     * @param vid      从视频
     * @return boolean
     * @throws IOErrorException ioerror异常那么
     */
    public static boolean deleteVertex(SessionWrapper session, boolean withEdge, String vid) throws IOErrorException {
        return deleteVertexBatch(session, withEdge, vid);
    }

    /**
     * 根据root vid 删除其关联的所有点
     *
     * @param session   会话
     * @param rootVid   根vid
     * @param startStep 开始步骤
     * @param endStep   最后一步
     * @param edges     边缘
     * @return boolean
     * @throws IOErrorException             ioerror异常那么
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static boolean deleteByGoRoot(SessionWrapper session, String rootVid, int startStep, int endStep, List<String> edges) throws IOErrorException, UnsupportedEncodingException {
        String query = buildGoRootQuery(rootVid, startStep, endStep, edges, GraphConstant.GO_DIRECTION_BIDIRECT, true);
        log(query);
        ResultSet execute = session.execute(query);
        return checkErr(execute);
    }

    /**
     * 批量删除点
     *
     * @param session  会话
     * @param withEdge 与边缘
     * @param vid      从视频
     * @return boolean
     * @throws IOErrorException ioerror异常那么
     */
    public static boolean deleteVertexBatch(SessionWrapper session, boolean withEdge, String... vid) throws IOErrorException {
        return deleteVertexBatch(session, withEdge, Arrays.asList(vid));
    }

    /**
     * 批量删除点
     *
     * @param session  会话
     * @param withEdge 与边缘
     * @param vidList  请看列表
     * @return boolean
     * @throws IOErrorException ioerror异常那么
     */
    public static boolean deleteVertexBatch(SessionWrapper session, boolean withEdge, Collection<String> vidList) throws IOErrorException {
        String deleteVertex = buildDeleteVertexQuery(withEdge, vidList);
        log(deleteVertex);
        ResultSet execute = session.execute(deleteVertex);
        return checkErr(execute);
    }

    /**
     * 构建更新或插入点语句
     *
     * @param vid      从视频
     * @param tagName  标签名
     * @param data     数据
     * @param operator 操作符
     * @return {@link String}
     */
    private static String doBuildUpdateOrUpsertTagQuery(String vid, String tagName, Map<String, Object> data, String operator) {
        data = JSONObject.parseObject(replaceAllFastJSONStringLineCharacters(JSONObject.toJSONString(data)));
        StringBuilder sb = new StringBuilder();
        sb.append(operator).append(" VERTEX ON ").append(tagName).append(" \"").append(vid).append("\" SET ");
        data.forEach((k, v) -> {
            sb.append(k).append(" = ");
            if (v instanceof Number) {
                sb.append(v).append(", ");
            } else {
                sb.append("\"").append(v).append("\",");
            }
        });
        sb.delete(sb.length() - 1, sb.length());
        return sb.toString();
    }

    /**
     * 构建 vid
     *
     * @param prefix 前缀
     * @param id     id
     * @return {@link String}
     */
    public static String buildVid(String prefix, Object... id) {
        return buildVid(prefix, Arrays.asList(id));
    }

    /**
     * 构建 vid
     *
     * @param prefix 前缀
     * @param idList id列表
     * @return {@link String}
     */
    public static String buildVid(String prefix, Collection<Object> idList) {
        StringBuilder sb = new StringBuilder();
        sb.append(prefix).append(StringPool.COLON);
        for (Object o : idList) {
            if (o instanceof String) {
                o = replaceAllLineCharacters(o);
            }
            sb.append(o).append(StringPool.UNDERSCORE);
        }
        sb.delete(sb.length() - 1, sb.length());
        return sb.toString();
    }

    /**
     * 构建路径查询
     *
     * @param request 请求
     * @return {@link String}
     */
    @Deprecated
    public static String buildPathQuery(GraphPathRequest request) {
        return buildPathQuery(request.getVid(), request.getSteps(), request.getLimit(), request.getDirection(), request.getEdges().toArray(new String[0]));
    }

    /**
     * 构建路径查询
     *
     * @param rootVid   根vid
     * @param steps     深
     * @param limit     限制
     * @param edges     标签名称
     * @param direction 方向  {@link ai.people.netmon.framework.constant.GraphDirection}
     * @return {@link String}
     */
    public static String buildPathQuery(String rootVid, int steps, Integer limit, String direction, String... edges) {
        StringBuilder sb = new StringBuilder();
        String from = null;
        String to = null;
        switch (direction) {
            case GraphDirection.BOTH:
                from = "-";
                to = "-";
                break;
            case GraphDirection.TO:
                from = "<-";
                to = "-";
                break;
            case GraphDirection.FROM:
            default:
                from = "-";
                to = "->";
                break;
        }
        sb.append("MATCH p=(v)").append(from).append("[e");
        if (Objects.nonNull(edges)) {
            for (int i = 0; i < edges.length; i++) {
                String edge = edges[i];
                if (StringUtils.isNotBlank(edge)) {
                    if (i == 0) {
                        sb.append(":");
                    } else {
                        sb.append("|:");
                    }
                    sb.append(edge);
                }
            }
        }
        sb.append("*0..").append(steps);
        sb.append("]").append(to).append("(v2)");
        sb.append(" WHERE id(v) == \"").append(rootVid).append("\"");
        sb.append(" RETURN DISTINCT p");
        if (limit != null) {
            sb.append(" LIMIT ").append(limit);
        }
        return sb.toString();
    }

    /**
     * 构建插入点查询语句
     *
     * @param tagName 标签名
     * @param vid     从视频
     * @param data    数据
     * @return {@link String}
     */
    public static String buildInsertVertexQuery(String tagName, String vid, Object data) {
        JSONObject obj = new JSONObject();
        if (data != null) {
            obj = JSONObject.parseObject(JSONObject.toJSONString(data));
        }
        StringBuilder sb = new StringBuilder();
        sb.append("INSERT VERTEX ").append(tagName).append("(");

        List<Object> objects = new ArrayList<>();
        if (obj != null && !obj.isEmpty()) {
            for (String s : obj.keySet()) {
                sb.append(s).append(",");
                objects.add(obj.get(s));
            }
            sb.delete(sb.length() - 1, sb.length());
        }

        sb.append(") VALUES \"").append(vid).append("\":(");
        if (objects.size() > 0) {
            for (Object object : objects) {
                if (object instanceof Number) {
                    sb.append(object).append(",");
                } else {
                    sb.append("\"").append(object).append("\",");
                }
            }
            sb.delete(sb.length() - 1, sb.length());
        }
        sb.append(")").append(StringPool.SEMICOLON);
        return sb.toString();
    }

    /**
     * 构建插入边查询语句
     *
     * @param srcVid    src vid
     * @param dstVid    dst vid
     * @param edgeName  边缘名字
     * @param edgeValue 边缘值
     * @return {@link String}
     */
    public static String buildCreateEdgeQuery(String srcVid, String dstVid, String edgeName, JSONObject edgeValue) {
        StringBuilder sb = new StringBuilder();
        sb.append("INSERT EDGE ").append(edgeName).append("(");
        List<Object> objects = new ArrayList<>();
        if (edgeValue != null && edgeValue.size() != 0) {
            for (String s : edgeValue.keySet()) {
                sb.append(s).append(",");
                objects.add(edgeValue.get(s));
            }
            sb.delete(sb.length() - 1, sb.length());
        }
        sb.append(") VALUES \"").append(srcVid).append("\"->\"").append(dstVid).append("\":(");
        if (objects.size() != 0) {
            for (Object object : objects) {
                if (object instanceof Number) {
                    sb.append(object).append(",");
                } else {
                    sb.append("\"").append(replaceAllLineCharacters(object)).append("\",");
                }
            }
            sb.delete(sb.length() - 1, sb.length());
        }
        sb.append(")").append(StringPool.SEMICOLON);
        return sb.toString();
    }

    /**
     * 构建插入或更新点查询语句
     *
     * @param vid     从视频
     * @param tagName 标签名
     * @param data    数据
     * @return {@link String}
     */
    public static String buildUpsertVertexQuery(String vid, String tagName, Map<String, Object> data) {
        return doBuildUpdateOrUpsertTagQuery(vid, tagName, data, "UPSERT");
    }


    /**
     * 构建更新点语句
     *
     * @param vid     从视频
     * @param tagName 标签名
     * @param data    数据
     * @return {@link String}
     */
    public static String buildUpdateVertexQuery(String vid, String tagName, Map<String, Object> data) {
        return doBuildUpdateOrUpsertTagQuery(vid, tagName, data, "UPDATE");
    }

    /**
     * 替换换行字符
     *
     * @param str str
     * @return {@link String}
     */
    private static String replaceAllLineCharacters(Object str) {
        return ((String) str).replaceAll("[\n\r]", GraphConstant.REPLACEMENT);
    }

    /**
     * 替换 json 中的换行字符
     *
     * @param str str
     * @return {@link String}
     */
    private static String replaceAllFastJSONStringLineCharacters(String str) {
        return str.replaceAll("\\\\n|\\\\r", GraphConstant.REPLACEMENT);
    }

   

    /**
     * 构建删除点查询语句
     *
     * @param withEdge 与边缘
     * @param vids     网络上
     * @return {@link String}
     */
    public static String buildDeleteVertexQuery(boolean withEdge, Collection<String> vids) {
        StringBuilder sb = new StringBuilder();
        sb.append("DELETE VERTEX ");
        for (String vid : vids) {
            sb.append("\"").append(vid).append("\",");
        }
        sb.delete(sb.length() - 1, sb.length());
        if (withEdge) {
            sb.append(" WITH EDGE");
        }
        return sb.toString();
    }

   

    /**
     * 构建删除边查询语句
     *
     * @param edgeCode 边缘代码
     * @param srcVid   src vid
     * @param dstVid   dst vid
     * @return {@link String}
     */
    public static String buildDeleteEdgeQuery(String edgeCode, String srcVid, String dstVid) {
        return "DELETE EDGE " + edgeCode + " " + srcVid + "->" + dstVid;
    }



    /**
     * 构建根去查询
     *
     * @param rootVid   根vid
     * @param startStep 开始步骤
     * @param endStep   最后一步
     * @param edges     边缘
     * @param direction 方向
     * @param del       ▽
     * @return {@link String}
     */
    public static String buildGoRootQuery(String rootVid, int startStep, int endStep, List<String> edges, String direction, boolean del) {
        StringBuilder sb = new StringBuilder();
        sb.append("GO ").append(startStep).append(" TO ").append(endStep).append(" steps FROM \"").append(rootVid).append("\" OVER ");
        if (CollectionUtils.isEmpty(edges)) {
            sb.append("*");
        } else {
            for (String edge : edges) {
                sb.append(edge).append(",");
            }
            sb.delete(sb.length() - 1, sb.length());
        }
        sb.append(" ").append(direction).append(" YIELD DISTINCT ").append("id($$) AS id");
        if (del) {
            sb.append(" | DELETE VERTEX $-.id WITH EDGE").append(StringPool.SEMICOLON);
        }
        return sb.toString();
    }

    /**
     * 构建路径查询
     *
     * @param vid       从视频
     * @param direction 方向
     * @return {@link String}
     */
    public static String buildPathQuery(Object vid, String direction) {
        StringBuilder query = new StringBuilder();
        query.append("MATCH (v)");
        switch (direction) {
            case GraphDirection.FROM:
                query.append("-[e*1]->");
                break;
            case GraphDirection.TO:
                query.append("<-[e*1]-");
                break;
            case GraphDirection.BOTH:
                query.append("-[e*1]-");
                break;
            default:
                break;
        }
        query.append("(v2) WHERE id(v) == \"").append(vid).append("\" RETURN DISTINCT v,e,v2");
        log(query.toString());
        return query.toString();
    }


    /**
     * 递归遍历图
     *
     * @param session     会话
     * @param vid         从视频
     * @param nebulaPaths 星云路径
     * @param set         集
     * @param direction   方向
     * @throws IOErrorException             ioerror异常那么
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static void recursionGetPath(SessionWrapper session, String vid, LinkedHashSet<NebulaGraph.NebulaPath> nebulaPaths, Set<String> set, String direction) throws IOErrorException, UnsupportedEncodingException {
        //如果已经遍历了这个点的，就跳过
        if (set.contains(vid)) {
            return;
        }
        String query = buildPathQuery(vid, direction);
        ResultSet resultSet = session.execute(query);
        int rowsSize = resultSet.rowsSize();
        if (rowsSize == 0) {
            return;
        }
        set.add(vid);
        for (int i = 0; i < rowsSize; i++) {
            ResultSet.Record valueWrappers = resultSet.rowValues(i);
            //按返回的别名顺序 v->起始节点 e-> 边 v2->终止节点
            ValueWrapper fromWrapper = valueWrappers.get(0);
            ValueWrapper edgeWrapper = valueWrappers.get(1);
            ValueWrapper toWrapper = valueWrappers.get(2);


            NebulaGraph.NebulaNode fromNode = doHandleVertex(fromWrapper);
            //vidList会储存关系中的起始和终止节点，在构建的时候以此来调整顺序
            List<String> vidList = new ArrayList<>();
            NebulaGraph.NebulaNode edge = doHandleEdge(edgeWrapper, vidList);
            NebulaGraph.NebulaNode toNode = doHandleVertex(toWrapper);

            String src = vidList.get(0);
            recursionGetPath(session, toNode.getVid().toString(), nebulaPaths, set, direction);
            //如果返回的第一个节点就是起始节点，那么from就还是起始节点，不需要调换位置，反之to是起始节点
            if (Objects.equals(src, vid)) {
                nebulaPaths.add(new NebulaGraph.NebulaPath(fromNode, toNode, edge));
            } else {
                nebulaPaths.add(new NebulaGraph.NebulaPath(toNode, fromNode, edge));
            }
        }
    }

    /**
     * 递归遍历图
     *
     * 会检查点是否在collection中
     *
     * @param session     会话
     * @param vid         从视频
     * @param nebulaPaths 星云路径
     * @param set         集
     * @param direction   方向
     * @param collection  集合
     * @throws IOErrorException             ioerror异常那么
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    public static void recursionGetPath(SessionWrapper session, String vid, LinkedHashSet<NebulaGraph.NebulaPath> nebulaPaths, Set<String> set, String direction, Collection collection) throws IOErrorException, UnsupportedEncodingException {
        //如果已经遍历了这个点的，就跳过
        if (set.contains(vid) || (CollectionUtils.isNotEmpty(collection) && !collection.contains(vid))) {
            return;
        }
        String query = buildPathQuery(vid, direction);
        ResultSet resultSet = session.execute(query);
        int rowsSize = resultSet.rowsSize();
        if (rowsSize == 0) {
            return;
        }
        set.add(vid);
        for (int i = 0; i < rowsSize; i++) {
            ResultSet.Record valueWrappers = resultSet.rowValues(i);
            //按返回的别名顺序 v->起始节点 e-> 边 v2->终止节点
            ValueWrapper fromWrapper = valueWrappers.get(0);
            ValueWrapper edgeWrapper = valueWrappers.get(1);
            ValueWrapper toWrapper = valueWrappers.get(2);


            NebulaGraph.NebulaNode fromNode = doHandleVertex(fromWrapper);
            //vidList会储存关系中的起始和终止节点，在构建的时候以此来调整顺序
            List<String> vidList = new ArrayList<>();
            NebulaGraph.NebulaNode edge = doHandleEdge(edgeWrapper, vidList);
            NebulaGraph.NebulaNode toNode = doHandleVertex(toWrapper);

            String src = vidList.get(0);
            recursionGetPath(session, toNode.getVid().toString(), nebulaPaths, set, direction, collection);
            if (CollectionUtils.isEmpty(collection) || (collection.contains(vid) && collection.contains(src))) {
                //如果返回的第一个节点就是起始节点，那么from就还是起始节点，不需要调换位置，反之to是起始节点
                if (Objects.equals(src, vid)) {
                    nebulaPaths.add(new NebulaGraph.NebulaPath(fromNode, toNode, edge));
                } else {
                    nebulaPaths.add(new NebulaGraph.NebulaPath(toNode, fromNode, edge));
                }
            }
        }
    }



    private static NebulaGraph.NebulaNode doHandleVertex(ValueWrapper value) throws UnsupportedEncodingException {
        if (value.isVertex()) {
            Node node = value.asNode();
            return new NebulaGraph.NebulaNode(node);
        }
        return null;
    }

    private static NebulaGraph.NebulaNode doHandleEdge(ValueWrapper value, List<String> vidList) throws UnsupportedEncodingException {
        if (value.isList()) {
            List<ValueWrapper> list = value.asList();
            ValueWrapper valueWrapper = list.get(0);
            if (valueWrapper.isEdge()) {
                Relationship relationship = valueWrapper.asRelationship();
                ValueWrapper dstId = relationship.dstId();
                ValueWrapper srcId = relationship.srcId();
                vidList.add(srcId.asString());
                vidList.add(dstId.asString());
                String edgeName = relationship.edgeName();
                HashMap<String, ValueWrapper> properties = relationship.properties();
                return new NebulaGraph.NebulaNode(edgeName, properties);
            }
        }
        return null;
    }


}
